
`include "common_header.verilog" 

//  *************************************************************************
//  File : ff_rctlpre_1d
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited. 
//  Copyright (c) 2003 Morethanip, GERMANY
//  info@morethanip.com
//  www.morethanip.com
//  *************************************************************************
//  Designed by : Daniel Koehler
//  *************************************************************************
//  Description: Control of FIFO Read Interface to Atlantic.
//               1 clock cycle delay from rdy to dval.
//  Version    : $Id: ff_rctlpre_1d.v,v 1.6 2011/12/16 10:26:19 dk Exp $
//  *************************************************************************
module ff_rctlpre_1d (

   reset_rclk,
   rclk,
   sw_reset_rclk,
   dff_aempty,
   dff_sav,
   dff_rden,
   sff_empty,
   sff_rden,
   sop_rden,
   frm_length,
   fdisc,
   ferr,
   fmod,
   fr_rden,
   fr_sop,
   fr_eop,
   fr_err,
   fr_mod,
   fr_dval,
   fr_sav);
   
parameter WLEN_WIDTH = 12;
parameter FF_WIDTH_MOD = 3;             // modulo bits
parameter MIN_DISTANCE_ENA = 0;         // 1: make eop->next frame sop distance minimal (if 0 it is 3 cycles in between frames)

input   reset_rclk;     //  Global Reset
input   rclk;           //  MAC Transmit Clock
input   sw_reset_rclk;  //  sync reset
input   dff_aempty;     //  data-fifo almost empty
input   dff_sav;        //  data-fifo section available
output  dff_rden;       //  data-fifo read enable
input   sff_empty;      //  Status FIFO Empty
output  sff_rden;       //  Status FIFO read
output  sop_rden;       // read sop fifo which contains preamble.
input   [WLEN_WIDTH - 1:0] frm_length; //  frame length (words)
input   fdisc;          //  discard frame
input   ferr;           //  propagate frame error
input   [FF_WIDTH_MOD-1:0] fmod;     //  last word modulo
input   fr_rden;        //  application read request
output  fr_sop;         //  SOP generated
output  fr_eop;         //  EOP generated
output  fr_err;         //  Error frame
output  [FF_WIDTH_MOD-1:0] fr_mod;  //  last word modulo
output  fr_dval;        //  read data valid
output  fr_sav;         //  fifo data available

wire    dff_rden; 
wire    sff_rden; 
reg     sop_rden;      // read sop fifo which contains preamble.
reg     fr_sop; 
wire    fr_eop; 
reg     fr_err; 
reg     [FF_WIDTH_MOD-1:0] fr_mod; 
reg     fr_dval; 
reg     fr_sav; 

// TYPE loc_state:
parameter LOC_STATE_IDLE     = 3'd 0;
parameter LOC_STATE_FLUSH    = 3'd 1;
parameter LOC_STATE_DATA     = 3'd 2;
parameter LOC_STATE_WAITS    = 3'd 3;
parameter LOC_STATE_END_FRM  = 3'd 4;
parameter LOC_STATE_NEXT_FRM = 3'd 5;

`define FFRCTL641D_HEX_VAL1 {{(WLEN_WIDTH-1){1'b 0}}, 1'b 1}


//  FIFO Read Control
//  -----------------
reg     [2:0] state; //  RST_DONE, B2B
reg     [2:0] nextstate; 
reg     [WLEN_WIDTH - 1:0] frm_cnt; 
reg     frm_cnt_dec; 
reg     flushing; //  frame is currently flushed

//  Data Available Indication when section of data or status fifo has data
//  ----------------------------------------------------------------------

always @(posedge reset_rclk or posedge rclk)
   begin : process_1
   if (reset_rclk == 1'b 1)
      begin
      fr_sav <= 1'b 0;	
      end
   else
      begin
      if ((dff_sav == 1'b 1 | sff_empty == 1'b 0) & 
	nextstate != LOC_STATE_FLUSH & nextstate != LOC_STATE_END_FRM & 
	nextstate != LOC_STATE_NEXT_FRM & flushing == 1'b 0)
         begin
         fr_sav <= 1'b 1;	
         end
      else
         begin
         fr_sav <= 1'b 0;	
         end
      end
   end

//  Length Counter
//  --------------
always @(posedge reset_rclk or posedge rclk)
   begin : process_2
   if (reset_rclk == 1'b 1)
      begin
      frm_cnt <= {(WLEN_WIDTH){1'b 0}};	
      end
   else
      begin
      if (nextstate == LOC_STATE_DATA)
         begin
         frm_cnt <= frm_cnt + `FFRCTL641D_HEX_VAL1;	
         end
      else if (state == LOC_STATE_END_FRM | state == LOC_STATE_IDLE)
         begin
        //  End of Packet Counter Reset
        //  ---------------------------
         frm_cnt <= {(WLEN_WIDTH){1'b 0}};	
         end
      end
   end

always @(posedge reset_rclk or posedge rclk)
   begin : process_3
   if (reset_rclk == 1'b 1)
      begin
      frm_cnt_dec <= 1'b 0;	
      end
   else
      begin
        
        //  End of Frame decoding - Single word frame
        //  -----------------------------------------
      if (state == LOC_STATE_IDLE & sff_empty == 1'b 0 & frm_length == 0 |      // == 1

        //  Don't lose during WAITS if we entered WAITS just one before end...
        //  ----------------------
        
        state == LOC_STATE_WAITS & frm_cnt_dec == 1'b 1 | 
        
        //  End of Frame Decoding
        //  --------------------------------------------------------
        
        //sff_empty == 1'b 0 & ((frm_length - frm_cnt) == `FFRCTL641D_HEX_VAL1))
        sff_empty == 1'b 0 & (frm_length==frm_cnt) |
        
        // software reset in middle of transaction
        // ---------------------------------------

        (state==LOC_STATE_DATA & sw_reset_rclk==1'b 1))
         begin
         frm_cnt_dec <= 1'b 1;	
         end
      else
         begin
         frm_cnt_dec <= 1'b 0;	
         end
      end
   end

//  Flush Indication
//  ----------------
always @(posedge reset_rclk or posedge rclk)
   begin : process_4
   if (reset_rclk == 1'b 1)
      begin
      flushing <= 1'b 0;	
      end
   else
      begin
        //  maintain flush flag
      if (nextstate == LOC_STATE_FLUSH)
         begin
         flushing <= 1'b 1;	
         end
      else if (nextstate == LOC_STATE_IDLE )
         begin
         flushing <= 1'b 0;	
         end
      end
   end

//  Main State Machine
//  ------------------
always @(posedge reset_rclk or posedge rclk)
   begin : process_5
   if (reset_rclk == 1'b 1)
      begin
      state <= LOC_STATE_IDLE;	
      end
   else
      begin
        //if( sw_reset_rclk==1'b 1 )
        //begin
        //        state <= LOC_STATE_IDLE;
        //end
        //else
        //begin
                state <= nextstate;	
        //end
      end
   end

always @(state or sff_empty or fdisc or flushing or fr_rden or dff_sav or dff_aempty or frm_cnt_dec or sw_reset_rclk)
   begin : process_6
   case (state)
   LOC_STATE_IDLE:
      begin
      if (sff_empty == 1'b 0 & fdisc == 1'b 1)
         begin
         nextstate = LOC_STATE_FLUSH;	
         end
      else if ((sff_empty == 1'b 0 | dff_sav == 1'b 1) & fr_rden == 1'b 1 & sw_reset_rclk==1'b 0)
         begin
         nextstate = LOC_STATE_DATA;	
         end
      else
         begin
         nextstate = LOC_STATE_IDLE;	
         end
      end

//  Frame Flush Marker
//  -----------
   LOC_STATE_FLUSH:
      begin
      nextstate = LOC_STATE_DATA;	
      end

//  FIFO Data Read
//  --------------                               
   LOC_STATE_DATA:
      begin
      if (frm_cnt_dec == 1'b 1)
         begin
         nextstate = LOC_STATE_END_FRM;	
         end

        //  Wait State - Data FIFO Empty or Application not Ready
        //  -----------------------------------------------------

      else if (sw_reset_rclk==1'b 0 & (flushing == 1'b 0 & fr_rden == 1'b 0 | dff_aempty == 1'b 1 & sff_empty == 1'b 1) )
         begin
         nextstate = LOC_STATE_WAITS;	
         end
      else
         begin
         nextstate = LOC_STATE_DATA;	
         end
      end

   LOC_STATE_END_FRM:
      begin
      nextstate = LOC_STATE_NEXT_FRM;	
      end

//  Additional Cycle on End of Frame - Status FIFO Latency
//  ------------------------------------------------------
   LOC_STATE_NEXT_FRM:
      begin
      if (MIN_DISTANCE_ENA==1 & flushing == 1'b 0 & ((sff_empty == 1'b 0 | dff_sav == 1'b 1) & fr_rden == 1'b 1 & sw_reset_rclk==1'b 0))
         begin
                // immediately restart reading. However, this will miss the fdisc, hence error discard will not occur.
         nextstate = LOC_STATE_DATA;	
         end
      else
         begin
         nextstate = LOC_STATE_IDLE;	
         end
      end
   LOC_STATE_WAITS:
      begin
      if (((dff_aempty == 1'b 0 | sff_empty == 1'b 0) & (fr_rden == 1'b 1 | flushing == 1'b 1)) | sw_reset_rclk==1'b 1 )
         begin
         nextstate = LOC_STATE_DATA;	//  frm_cnt_dec has been preserved!
         end
      else
         begin
         nextstate = LOC_STATE_WAITS;	
         end
      end

   default:
      begin
      nextstate = LOC_STATE_IDLE;	
      end

   endcase
   end

//  Data Read from FIFO when State Machine Goes in DATA State
//  ---------------------------------------------------------               
assign dff_rden = nextstate == LOC_STATE_DATA ? 1'b 1 : 1'b 0; 

//  Descriptor Read when State Machine Exits Data State
//  ---------------------------------------------------                 
assign sff_rden = nextstate == LOC_STATE_END_FRM ? 1'b 1 : 1'b 0; 

//  Data Provided to Application when State Machine Goes in DATA State
//  ------------------------------------------------------------------
always @(posedge reset_rclk or posedge rclk)
   begin : process_7
   if (reset_rclk == 1'b 1)
      begin
      fr_dval <= 1'b 0;	
      end
   else
      begin

      if (nextstate == LOC_STATE_DATA & flushing == 1'b 0)
         begin
         fr_dval <= 1'b 1;	
         end
      else
         begin
         fr_dval <= 1'b 0;	
         end

      end
   end

//  Control Signals
//  ---------------
always @(posedge reset_rclk or posedge rclk)
   begin : process_8
   if (reset_rclk == 1'b 1)
      begin
      fr_sop <= 1'b 0;	
      //fr_eop <= 1'b 0;	
      fr_err <= 1'b 0;	
      fr_mod <= {FF_WIDTH_MOD{1'b 0}};	
      end
   else
      begin
        //  generate frame delimiters
      if (state == LOC_STATE_IDLE & nextstate == LOC_STATE_DATA |
          state == LOC_STATE_NEXT_FRM & nextstate == LOC_STATE_DATA )
         begin
         fr_sop <= 1'b 1;	
         end
      else
         begin
         fr_sop <= 1'b 0;	
         end

      //if (nextstate == LOC_STATE_END_FRM & flushing == 1'b 0)
      //   begin
      //   fr_eop <= 1'b 1;	
      //   end
      //else
      //   begin
      //   fr_eop <= 1'b 0;	
      //   end

      //if (nextstate == LOC_STATE_END_FRM & flushing == 1'b 0)
      if ((nextstate == LOC_STATE_DATA | nextstate == LOC_STATE_END_FRM) & sff_empty == 1'b 0 & flushing == 1'b 0)
         begin
         fr_mod <= fmod;	
         end
      else
         begin
         fr_mod <= {FF_WIDTH_MOD{1'b 0}};	
         end

        //  propagate error indication

      if ( ((nextstate == LOC_STATE_DATA | nextstate == LOC_STATE_END_FRM) & flushing == 1'b 0 & sff_empty == 1'b 0 & ferr == 1'b 1) |
            (state==LOC_STATE_DATA & sw_reset_rclk==1'b 1) )
         begin
         fr_err <= 1'b 1;	
         end
      else
         begin
         fr_err <= 1'b 0;	
         end

      end
   end

assign fr_eop = frm_cnt_dec & ~flushing; // (nextstate == LOC_STATE_END_FRM & flushing == 1'b 0) ? 1'b 1 : 1'b 0;

// -------------------
// read preamble/timestamp fifo after SOP was delivered to application
// -------------------

always @(posedge reset_rclk or posedge rclk)
   begin : prb_rd
   if (reset_rclk == 1'b 1)
      begin
      sop_rden <= 1'b 0;	
      end
   else
      begin

        //  same rules as fr_sop, but additionally must read when discard happens.

      if (state == LOC_STATE_IDLE & nextstate == LOC_STATE_DATA |
          state == LOC_STATE_NEXT_FRM & nextstate == LOC_STATE_DATA |
          nextstate == LOC_STATE_FLUSH)
         begin
         sop_rden <= 1'b 1;	
         end
      else
         begin
         sop_rden <= 1'b 0;	
         end
         
      end
      
   end


// -------------
// ASSERTIONS
// -------------
`ifdef MTIPASSERTIONS
// synopsys translate_off
// synthesis translate_off

always @(posedge reset_rclk or posedge rclk)
begin:assertions

        if (reset_rclk == 1'b 1)
        begin
        end
        else
        begin
                if (sff_empty == 1'b 0 && (frm_length<frm_cnt) && frm_cnt_dec==1'b 0 && nextstate==LOC_STATE_DATA)
                begin
                    $display("%t ASSERT ***[ff_rctlpre_1d:%m]: ERROR: RX FIFO read control corruption occured (missed frm_cnt_dec). FIFO corrupted", $time());
                    `ifdef MTIPASSERTIONS_STOP_ON_ERRORS
                    $stop;
                    `endif
                end
                
        end

end

// synthesis translate_on
// synopsys translate_on
`endif


endmodule // module ff_rctl64
